/*
 * edbd_basefunc.c - base functions
 * Clunix, cezanne@clunix.com, 2005
 *
 * Copyright (c) 2005 by Clunix Inc. All Rights Reserved.
 */
#include "config.h"

#include "edbd.h"

#ifdef BDEXPR_MKENTITY

#define EDBD_BASEFUNC_EQUAL_REF		NULL
#define EDBD_BASEFUNC_EQUAL		NULL
#define EDBD_BASEFUNC_NOTEQUAL_REF	NULL
#define EDBD_BASEFUNC_NOTEQUAL		NULL

#define EDBD_BASEFUNC_PLUSFUNC		NULL
#define EDBD_BASEFUNC_MINUSFUNC		NULL

#define EDBD_BASEFUNC_GREATERTHAN	NULL
#define EDBD_BASEFUNC_LESSTHAN		NULL
#define EDBD_BASEFUNC_GREATEREQUAL	NULL
#define EDBD_BASEFUNC_LESSEQUAL		NULL

#define EDBD_BASEFUNC_LOGICALNOT	NULL

#else

#include "edbd_sr.h"
#include "edbd_vsr.h"

#define EDBD_BASEFUNC_EQUAL_REF		basefunc_equal_ref
#define EDBD_BASEFUNC_EQUAL		basefunc_equal
#define EDBD_BASEFUNC_NOTEQUAL_REF	basefunc_notequal_ref
#define EDBD_BASEFUNC_NOTEQUAL		basefunc_notequal

#define EDBD_BASEFUNC_PLUSFUNC		basefunc_plusfunc
#define EDBD_BASEFUNC_MINUSFUNC		basefunc_minusfunc

#define EDBD_BASEFUNC_GREATERTHAN	basefunc_greaterthan
#define EDBD_BASEFUNC_LESSTHAN		basefunc_lessthan
#define EDBD_BASEFUNC_GREATEREQUAL	basefunc_greaterequal
#define EDBD_BASEFUNC_LESSEQUAL		basefunc_lessequal

#define EDBD_BASEFUNC_LOGICALNOT	basefunc_logicalnot

#endif

#define AT_RUNTIME	0

#define NEG_BOOL(a)	((a) ? ECM_FALSE: ECM_TRUE)

#ifndef BDEXPR_MKENTITY

static ECM_INLINE ECMBOOL
check_equal_ref(const sr_t *sr_args)
{
	sr_t	*sr_oprnd1 = sr_get_idx(sr_args, 0);
	sr_t	*sr_oprnd2 = sr_get_idx(sr_args, 1);

	return sr_is_same_sr_LNP(edbd_sr_get_ref(sr_oprnd1),
				 edbd_sr_get_ref(sr_oprnd2));
}

static ECM_INLINE ECMBOOL
check_equal(const sr_t *sr_args)
{
	sr_t	*sr_oprnd1 = vsr_get_idx(sr_args, 0);
	sr_t	*sr_oprnd2 = vsr_get_idx(sr_args, 1);
	srt_t	bt = vsr_get_base_type(sr_oprnd1);
	ECMBOOL	equal = ECM_FALSE;

	switch (bt) {
	case SR_BT_BOOL:
		if (edbd_sr_get_bool(sr_oprnd1) == edbd_sr_get_bool(sr_oprnd2))
			equal = ECM_TRUE;
		break;
	case SR_BT_INT:
		if (edbd_sr_get_int(sr_oprnd1) == edbd_sr_get_int(sr_oprnd2))
			equal = ECM_TRUE;
		break;
	case SR_BT_LL:
		if (edbd_sr_get_ll(sr_oprnd1) == edbd_sr_get_ll(sr_oprnd2))
			equal = ECM_TRUE;
		break;
	case SR_BT_DBL:
		if (edbd_sr_get_dbl(sr_oprnd1) == edbd_sr_get_dbl(sr_oprnd2))
			equal = ECM_TRUE;
		break;
	case SR_BT_STR:
		if (ecm_strcmp(edbd_sr_get_str(sr_oprnd1),
			       edbd_sr_get_str(sr_oprnd2)) == 0)
			equal = ECM_TRUE;
		break;
	case SR_BT_OCT:	{
		int	len1, len2;

		len1 = edbd_sr_get_oct_size(sr_oprnd1);
		len2 = edbd_sr_get_oct_size(sr_oprnd2);
		if (len1 == len2 &&
		    ecm_memcmp(edbd_sr_get_oct_data(sr_oprnd1),
			       edbd_sr_get_oct_data(sr_oprnd2),
			       len1) == 0)
			equal = ECM_TRUE;
		break;
	}
	}
	return equal;
}

static sr_t *
basefunc_equal_ref(const sr_t *sr_args)
{
	return sr_create_bool(ST_BOOL, check_equal_ref(sr_args));
}

static sr_t *
basefunc_equal(const sr_t *sr_args)
{
	return sr_create_bool(ST_BOOL, check_equal(sr_args));
}

static sr_t *
basefunc_notequal_ref(const sr_t *sr_args)
{
	ECMBOOL	res = check_equal_ref(sr_args);
	return sr_create_bool(ST_BOOL, NEG_BOOL(res));
}

static sr_t *
basefunc_notequal(const sr_t *sr_args)
{
	ECMBOOL	res = check_equal(sr_args);
	return sr_create_bool(ST_BOOL, NEG_BOOL(res));
}

#endif

static srt_t equalfunc_args1[] = { ST_NULL | SR_REF, ST_NULL | SR_REF };
static srt_t equalfunc_args2[] = { ST_NULL, ST_NULL };

static funcsig_t equalfunc_sigs[] = {
	{ EDBD_BASEFUNC_EQUAL_REF, 2, equalfunc_args1, ST_BOOL, },
	{ EDBD_BASEFUNC_EQUAL, 2, equalfunc_args2, ST_BOOL, }
};

static srt_t notequalfunc_args1[] = { ST_NULL | SR_REF, ST_NULL | SR_REF };
static srt_t notequalfunc_args2[] = { ST_NULL, ST_NULL };

static funcsig_t notequalfunc_sigs[] = {
	{ EDBD_BASEFUNC_NOTEQUAL_REF, 2, notequalfunc_args1, ST_BOOL },
	{ EDBD_BASEFUNC_NOTEQUAL, 2, notequalfunc_args2, ST_BOOL }
};

#ifndef BDEXPR_MKENTITY

static sr_t *
basefunc_plusfunc(const sr_t *sr_args)
{
	sr_t	*sr_oprnd1 = sr_get_idx(sr_args, 0);
	sr_t	*sr_oprnd2 = sr_get_idx(sr_args, 1);
	srt_t	srt;

	srt = vsr_get_base_type(sr_oprnd1);

	switch (srt) {
	case ST_STR: {
		char	*str;

		ecm_asprintf(&str, "%s%s",
			     ecm_safe_str(edbd_sr_get_str(sr_oprnd1)),
			     ecm_safe_str(edbd_sr_get_str(sr_oprnd2)));
		return sr_set_val(sr_create(ST_STR), sr_val_str(str));
	}
	case ST_INT:
		return sr_create_int(srt, edbd_sr_get_int(sr_oprnd1) + edbd_sr_get_int(sr_oprnd2));
	case ST_LL:
		return sr_create_ll(srt, edbd_sr_get_ll(sr_oprnd1) + edbd_sr_get_ll(sr_oprnd2));
	case ST_DBL:
		return sr_create_dbl(srt, edbd_sr_get_dbl(sr_oprnd1) + edbd_sr_get_dbl(sr_oprnd2));
	default:
		if (srt == ST_STATUS) {
			int	status1, status2;

			status1 = edbd_sr_get_int(sr_oprnd1);
			status2 = edbd_sr_get_int(sr_oprnd2);
			if (status1 == ST_STATUS_UNKNOWN && status2 == ST_STATUS_UNKNOWN)
				return sr_create_int(ST_STATUS, ST_STATUS_UNKNOWN);
			else if (status1 == ST_STATUS_UP && status2 == ST_STATUS_UP)
				return sr_create_int(ST_STATUS, ST_STATUS_UP);
			else if (status1 == ST_STATUS_UP || status2 == ST_STATUS_UP)
				return sr_create_int(ST_STATUS, ST_STATUS_PARTIAL);
			return sr_create_int(ST_STATUS, ST_STATUS_DOWN);
		}
		break;
	}
	return NULL;
}

static sr_t *
basefunc_minusfunc(const sr_t *sr_args)
{
	sr_t	*sr_oprnd1 = sr_get_idx(sr_args, 0);
	sr_t	*sr_oprnd2 = sr_get_idx(sr_args, 1);
	srt_t	srt, bt;

	srt = vsr_get_type(sr_oprnd1);
	bt = srt_get_base_type(srt);

	switch (bt) {
	case SR_BT_INT:
		return sr_create_int(srt, edbd_sr_get_int(sr_oprnd1) - edbd_sr_get_int(sr_oprnd2));
	case SR_BT_LL:
		return sr_create_ll(srt, edbd_sr_get_ll(sr_oprnd1) - edbd_sr_get_ll(sr_oprnd2));
	case SR_BT_DBL:
		return sr_create_dbl(srt, edbd_sr_get_dbl(sr_oprnd1) - edbd_sr_get_dbl(sr_oprnd2));
	}
	return NULL;
}

#endif

static srt_t	plusfunc_args1[] = { ST_STR, ST_STR };
static srt_t	plusfunc_args2[] = { ST_INT, ST_INT };
static srt_t	plusfunc_args3[] = { ST_LL, ST_LL };
static srt_t	plusfunc_args4[] = { ST_DBL, ST_DBL };
static srt_t	plusfunc_args5[] = { AT_RUNTIME, AT_RUNTIME };
static funcsig_t plusfunc_sigs[] = {
	{ EDBD_BASEFUNC_PLUSFUNC, 2, plusfunc_args1, ST_STR },
	{ EDBD_BASEFUNC_PLUSFUNC, 2, plusfunc_args2, ST_INT },
	{ EDBD_BASEFUNC_PLUSFUNC, 2, plusfunc_args3, ST_LL },
	{ EDBD_BASEFUNC_PLUSFUNC, 2, plusfunc_args4, ST_DBL },
	{ EDBD_BASEFUNC_PLUSFUNC, 2, plusfunc_args5, AT_RUNTIME },
};

static srt_t	minusfunc_args1[] = { ST_INT, ST_INT };
static srt_t	minusfunc_args2[] = { ST_LL, ST_LL };
static srt_t	minusfunc_args3[] = { ST_DBL, ST_DBL };
static funcsig_t minusfunc_sigs[] = {
	{ EDBD_BASEFUNC_MINUSFUNC, 2, minusfunc_args1, ST_INT },
	{ EDBD_BASEFUNC_MINUSFUNC, 2, minusfunc_args2, ST_LL },
	{ EDBD_BASEFUNC_MINUSFUNC, 2, minusfunc_args3, ST_DBL }
};

#ifndef BDEXPR_MKENTITY

static ECM_INLINE ECMBOOL
check_greaterthan(const sr_t *sr_args)
{
	sr_t	*sr_oprnd1 = sr_get_idx(sr_args, 0);
	sr_t	*sr_oprnd2 = sr_get_idx(sr_args, 1);

	if (edbd_sr_cmp_val(sr_oprnd1, sr_oprnd2) > 0)
		return ECM_TRUE;
	return ECM_FALSE;
}

static ECM_INLINE ECMBOOL
check_lessthan(const sr_t *sr_args)
{
	sr_t	*sr_oprnd1 = sr_get_idx(sr_args, 0);
	sr_t	*sr_oprnd2 = sr_get_idx(sr_args, 1);

	if (edbd_sr_cmp_val(sr_oprnd1, sr_oprnd2) < 0)
		return ECM_TRUE;
	return ECM_FALSE;
}

static sr_t *
basefunc_greaterthan(const sr_t *sr_args)
{
	return sr_create_bool(ST_BOOL, check_greaterthan(sr_args));
}

static sr_t *
basefunc_lessthan(const sr_t *sr_args)
{
	return sr_create_bool(ST_BOOL, check_lessthan(sr_args));
}

static sr_t *
basefunc_greaterequal(const sr_t *sr_args)
{
	ECMBOOL	res = check_lessthan(sr_args);
	return sr_create_bool(ST_BOOL, NEG_BOOL(res));
}

static sr_t *
basefunc_lessequal(const sr_t *sr_args)
{
	ECMBOOL	res = check_greaterthan(sr_args);
	return sr_create_bool(ST_BOOL, NEG_BOOL(res));
}

#endif

static srt_t greaterthan_args1[] = { ST_INT, ST_INT };
static srt_t greaterthan_args2[] = { ST_LL, ST_LL };
static srt_t greaterthan_args3[] = { ST_DBL, ST_DBL };
static srt_t greaterthan_args4[] = { ST_STR, ST_STR };
static funcsig_t greaterthan_sigs[] = {
	{ EDBD_BASEFUNC_GREATERTHAN, 2, greaterthan_args1, ST_BOOL },
	{ EDBD_BASEFUNC_GREATERTHAN, 2, greaterthan_args2, ST_BOOL },
	{ EDBD_BASEFUNC_GREATERTHAN, 2, greaterthan_args3, ST_BOOL },
	{ EDBD_BASEFUNC_GREATERTHAN, 2, greaterthan_args4, ST_BOOL },
};

static srt_t lessthan_args1[] = { ST_INT, ST_INT };
static srt_t lessthan_args2[] = { ST_LL, ST_LL };
static srt_t lessthan_args3[] = { ST_DBL, ST_DBL };
static srt_t lessthan_args4[] = { ST_STR, ST_STR };
static funcsig_t lessthan_sigs[] = {
	{ EDBD_BASEFUNC_LESSTHAN, 2, lessthan_args1, ST_BOOL },
	{ EDBD_BASEFUNC_LESSTHAN, 2, lessthan_args2, ST_BOOL },
	{ EDBD_BASEFUNC_LESSTHAN, 2, lessthan_args3, ST_BOOL },
	{ EDBD_BASEFUNC_LESSTHAN, 2, lessthan_args4, ST_BOOL }
};

static srt_t greaterequal_args1[] = { ST_INT, ST_INT };
static srt_t greaterequal_args2[] = { ST_LL, ST_LL };
static srt_t greaterequal_args3[] = { ST_DBL, ST_DBL };
static srt_t greaterequal_args4[] = { ST_STR, ST_STR };
static funcsig_t greaterequal_sigs[] = {
	{ EDBD_BASEFUNC_GREATEREQUAL, 2, greaterequal_args1, ST_BOOL },
	{ EDBD_BASEFUNC_GREATEREQUAL, 2, greaterequal_args2, ST_BOOL },
	{ EDBD_BASEFUNC_GREATEREQUAL, 2, greaterequal_args3, ST_BOOL },
	{ EDBD_BASEFUNC_GREATEREQUAL, 2, greaterequal_args4, ST_BOOL },
};

static srt_t lessequal_args1[] = { ST_INT, ST_INT };
static srt_t lessequal_args2[] = { ST_LL, ST_LL };
static srt_t lessequal_args3[] = { ST_DBL, ST_DBL };
static srt_t lessequal_args4[] = { ST_STR, ST_STR };
static funcsig_t lessequal_sigs[] = {
	{ EDBD_BASEFUNC_LESSEQUAL, 2, lessequal_args1 , ST_BOOL },  
	{ EDBD_BASEFUNC_LESSEQUAL, 2, lessequal_args2 , ST_BOOL },
	{ EDBD_BASEFUNC_LESSEQUAL, 2, lessequal_args3 , ST_BOOL },
	{ EDBD_BASEFUNC_LESSEQUAL, 2, lessequal_args4 , ST_BOOL }
};

#ifndef BDEXPR_MKENTITY

static sr_t *
basefunc_logicalnot(const sr_t *sr_args)
{
	return sr_create_bool(ST_BOOL, NEG_BOOL(edbd_sr_get_bool(sr_geti(sr_args, 0))));
}

#endif

static srt_t logicalnot_args1[] = { ST_NULL };
static funcsig_t logicalnot_sigs[] = {
	{ EDBD_BASEFUNC_LOGICALNOT, 1, logicalnot_args1, ST_BOOL }
};

static sr_t *
basefunc_username(const sr_t *sr_args)
{
#ifdef BDEXPR_MKENTITY
	return NULL;
#else
	return sr_create_str(ST_STR, edbd_conn_getuser(edbd_conn_get(ECM_FALSE)));
#endif
}

static funcsig_t username_sigs[] = {
	{ basefunc_username, 0, NULL, ST_STR },
};

static sr_t *
basefunc_is_admin(const sr_t *sr_args)
{
#ifdef BDEXPR_MKENTITY
	return NULL;
#else
	return sr_create_bool(ST_BOOL, edbd_auth_is_admin(edbd_conn_getuser(edbd_conn_get(ECM_FALSE))));
#endif
}

static funcsig_t is_admin_sigs[] = {
	{ basefunc_is_admin, 0, NULL, ST_BOOL },
};

static sr_t *
basefunc_LNP(const sr_t *sr_args)
{
	const char	*LNP;
	sr_val_t	srv;

	LNP = sr_geti_str(sr_args, 0);
	if (sr_casrv_scan(LNP, ST_LNP, &srv))
		return srv.dir;
	return NULL;
}

static srt_t LNP_args[] = { ST_STR };
static funcsig_t LNP_sigs[] = {
	{ basefunc_LNP, 1, LNP_args, ST_LNP },
};

static sr_t *
basefunc_max(const sr_t *sr_args)
{
	sr_t	*sr_ints, *sr_int;
	int	max, ival;

	sr_ints = sr_geti(sr_args, 0);
	if (sr_get_num_child(sr_ints) == 0)
		return NULL;

	sr_int = sr_first(sr_ints);
	max = sr_get_int(sr_int);
	for (; sr_int; sr_int = sr_next(sr_int)) {
		ival = sr_get_int(sr_int);
		if (ival > max)
			max = ival;
	}

	return sr_create_int(ST_INT, max);
}

static srt_t max_args1[] = { ST_INT }; /* TODO: */
static funcsig_t max_sigs[] = {
	{ basefunc_max, 1, max_args1, ST_INT }
};

static funcdef_t base_funcs[] = {
	{ NULL, 1, 2, equalfunc_sigs },
	{ NULL, 2, 2, notequalfunc_sigs },
	{ NULL, 3, 5, plusfunc_sigs },
	{ NULL, 4, 3, minusfunc_sigs },
	{ NULL, 5, 4, greaterthan_sigs },
	{ NULL, 6, 4, lessthan_sigs },
	{ NULL, 7, 4, greaterequal_sigs },
	{ NULL, 8, 4, lessequal_sigs },
	{ NULL, 9, 1, logicalnot_sigs },
	{ "username", 10, 1, username_sigs },
	{ "is_admin", 11, 1, is_admin_sigs },
	{ "LNP", 12, 1, LNP_sigs },
	{ "max", 13, 1, max_sigs },
};

#define N_BASEFUNCS	(sizeof(base_funcs) / sizeof(funcdef_t))

int
find_base_fid(const char *funcname)
{
	funcdef_t	*funcdef;
	int		i;

	for (i = 0; i < N_BASEFUNCS; i++) {
		funcdef = base_funcs + i;
		if (ecm_strcmp(funcdef->name, funcname) == 0)
			return BASE_FUNC_FID(i + 1);
	}
	return EDS_INVALID_FID;
}

ECM_INLINE funcdef_t *
base_fid_to_funcdef(eds_fid_t fid)
{
	if (EDS_IS_INVALID_FID(fid) || FID_TO_IDX(fid) > N_BASEFUNCS)
		return NULL;
	return base_funcs + FID_TO_IDX(fid) - 1;
}

void
edbd_basefunc_init(void)
{
	plusfunc_args5[0] = ST_STATUS;
	plusfunc_args5[1] = ST_STATUS;
	plusfunc_sigs[4].srt_ret = ST_STATUS;
}
